Skip to content

Fix Panorama segfault#5200

Closed
aknayar wants to merge 11 commits into
facebookresearch:mainfrom
aknayar:segfault-fix
Closed

Fix Panorama segfault#5200
aknayar wants to merge 11 commits into
facebookresearch:mainfrom
aknayar:segfault-fix

Conversation

@aknayar
Copy link
Copy Markdown
Contributor

@aknayar aknayar commented May 8, 2026

In the current code you can segfault Panorama indexes by loading a dataset with 960 dims and setting nlevels to 128.

In the case where nlevels does not evenly divide d, Panorama currently calculates level_width = ceil(d / nlevels). We cannot change this behavior due to backwards compatibility. However, for our case of 960 dims and 128 levels, this would mean each level is 8 dims. Thus, the last 8 levels have 0 dims each. Since we calculate actual_level_width using size_t params, it overflows and produces 8 when it should produce 0, leading to out-of-bounds memory accesses.

The fix is to simply truncate nlevels in the Panorama object when this would happen. A debug log is emitted to the user when this happens. In the aforementioned example, the user would see WARNING truncating nlevels from 128 to 120.

This PR also fixes a similar latent bug in IndexFlatPanorama::reconstruct where we would perform out-of-bounds memory accesses when nlevels doesn't evenly divide d.

Existing test cases are strengthened to ensure bugs are fixed and to avoid any future regressions.

@meta-cla meta-cla Bot added the CLA Signed label May 8, 2026
Comment thread faiss/IndexHNSW.cpp
: IndexHNSWFlat(), cum_sums(), pano(0, 1, 1), num_panorama_levels(0) {}
: IndexHNSWFlat(),
cum_sums(),
pano(sizeof(float), 1, 1),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needed so we don't have a division by zero when we do size_t n_real_levels = d / level_width_floats.

@aknayar
Copy link
Copy Markdown
Contributor Author

aknayar commented May 9, 2026

@mnorris11 Any advice on these OOMing tests? Is it something on my end?

Edit: My new tests were too beefy

@meta-codesync
Copy link
Copy Markdown
Contributor

meta-codesync Bot commented May 12, 2026

@mnorris11 has imported this pull request. If you are a Meta employee, you can view this in D104764705.

@mnorris11
Copy link
Copy Markdown
Contributor

In the case where nlevels does not evenly divide d, Panorama currently calculates level_width = ceil(d / nlevels). We cannot change this behavior due to backwards compatibility.

Thanks for the fix, can you explain this part more?

@aknayar
Copy link
Copy Markdown
Contributor Author

aknayar commented May 12, 2026

@mnorris11 Yup! Consider the case where d = 960 and nlevels = 128. Ideally we would have level_width = floor(960 / 128) = 7 dims per level. This would allow us to have 127 levels at level_width 7 and the 128th level would take the remaining (960 - 127 * 7 = 71) dims, satisfying the user's request for 128 levels.

However, the existing logic in the codebase is level_width = ceil(d / nlevels) instead of floor. This means level_width = 8 for this configuration. Since we transpose the storage layout to be level-major, we need to maintain this level_width of 8 for backwards compatibility (we cannot switch level_width to use floor). Loading an index which was serialized with a level_width of 8 into an index that expects a level_width of 7 will lead to bogus results.

Thus, if the user requests a configuration like d = 960, nlevels = 128, we have to truncate nlevels to be something that can actually accommodate the available dims given a level_width of ceil(d / nlevels). If backwards compatibility weren't a concern, we would rewrite this logic entirely.

@meta-codesync
Copy link
Copy Markdown
Contributor

meta-codesync Bot commented May 22, 2026

@mnorris11 merged this pull request in dd3003e.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants